/**
 * 能力
 * 1. 请求链路上：`参数管理` --> `发起请求` --> `请求完成` --> `页面渲染`
 * 2. 合并插件能力 (middlewares & props)
 * 3. 暴露哪些 props
 */
import { useEffect, useMemo, useRef } from "react";
import checker, { isFunction } from "./checker";
import composer, { onionCompose, pipeMergedCompose } from "./compose";
import createPluginManager from "./plugin";
import { IS_NATIVE_SYMBOL } from "./symbol";
import { IApp, Obj, Plugins, RawPlugins } from "./type";

export * from "./checker";
export * from "./type";


// export { PAYLOAD_SYMBOL, REQUEST_SYMBOL };

const useInit = fn => {
  const ref = useRef(true);
  if (ref.current) {
    ref.current = false;
    fn();
  }
};

const createQuery = (ctx: IApp["ctx"], plugins: Plugins) => {
  return () => {
    let run = onionCompose((plugins.middlewares as any) || []);
    let res = run(ctx);

    return res;
  };
};

const useCalcPlugin = (pluginManagerContext: {
  app: IApp;
  rawPlugins?: RawPlugins;
  props?: Obj;
  plugins?: Plugins;
}): Obj => {
  const pluginManager = useMemo(createPluginManager, []);

  useInit(() => {
    pluginManager.context.set(pluginManagerContext);
  });
  // 每次执行都重新计算插件
  pluginManager.pureUse(pluginManagerContext.rawPlugins);
  const plugins = useMemo(pluginManager.get, []);

  const pluginsProps = plugins.props
    .filter(prop => {
      return prop[IS_NATIVE_SYMBOL];
    })
    .reverse()
    .concat(
      plugins.props.reduce((acc, prop) => {
        if (prop[IS_NATIVE_SYMBOL]) {
          return acc;
        } else {
          return acc.concat(
            isFunction(prop) ? prop(pluginManagerContext.app.ctx) : prop
          );
        }
      }, [])
    );

  // 合并props
  const props = pipeMergedCompose(pluginsProps);

  pluginManagerContext.plugins = plugins;
  pluginManagerContext.props = props;

  return pluginManagerContext;
};

const usePlugin = rawPlugins => {
  const app: IApp = useMemo(
    () => ({
      ctx: {
        rightMenu:[]
      },
      helper: {
        ...checker,
        ...composer
      },
      actions: {}
    }),
    []
  );

  const { props, plugins } = useCalcPlugin({
    rawPlugins: rawPlugins.map(pl => pl(app)),
    app
  });

  const run = useMemo(() => createQuery(app.ctx, plugins), [plugins]);

  app.run = run;

  useEffect(() => {
    run();
  }, []);

  useUpdateEffect(run, []);

  return props;
};

export default usePlugin;

export const useStateIsInit = () => {
  const ref = useRef(true);
  if (ref.current) {
    ref.current = false;
    return true;
  }

  return ref.current;
};

export const useUpdateEffect = (fn, deps) => {
  const isInit = useStateIsInit();

  useEffect(() => {
    if (!isInit) {
      fn();
    }
  }, deps);
};
